<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html>
<head>
<title>Custom widget in Qyoto</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="GUI, Qyoto, Qt, custom widget, Visual Basic">
<meta name="description" content="Custom widget in Qyoto">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>


<div class="content">

<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>Custom widget in Qyoto</h1>

<p>
In this part of the Visual Basic Qyoto programming tutorial, we will 
create a custom widget. 
</p>

<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div> 

<p>
Toolkits usually provide only the most common widgets like buttons, 
text widgets, sliders etc. No toolkit can provide all possible widgets. 
Programmers must create such widgets by themselves. 
They do it by using the drawing tools provided by the toolkit. 
There are two possibilities. A programmer can modify or enhance an 
existing widget. Or he can create a custom widget from scratch. 
</p>


<h2>The Burning widget</h2>

<p>
In the next example, we will create a custom burning widget. 
This widget can be seen in applications like Nero or K3B. The widget will be 
created from scratch. 
</p>

<div class="codehead">burning.vb</div>
<pre class="code">
Imports Qyoto

NameSpace Burning

Public Class Burning 
    Inherits QWidget

    Const PANEL_HEIGHT As Integer = 30
    Const DISTANCE As Integer = 19
    Const LINE_WIDTH As Integer = 5
    Const DIVISIONS As Integer = 10
    
    Const FULL_CAPACITY As Double = 700.0
    Const MAX_CAPACITY As Double = 750.0
    
    Dim redColor As New QColor(255, 175, 175)
    Dim yellowColor As New QColor(255, 255, 184)

    Dim parent As QWidget 
    
    Dim num() As String = { _
        "75", "150", "225", "300", _
        "375", "450", "525", "600", _
        "675" _
    }
    
    Public Sub New(ByVal parent As QWidget) 
        Me.parent = parent
        MinimumHeight = PANEL_HEIGHT
    End Sub
    

    Protected Overrides Sub PaintEvent(ByVal e As QPaintEvent) 

        Dim painter As New QPainter(Me)
        
        Me.DrawWidget(painter)
        painter.End()
        
    End Sub

    Private Sub DrawWidget(ByVal painter As QPainter) 
        
        Dim burn As CustomWidget.VBQApp = CType(parent, CustomWidget.VBQApp)

        Dim slid_width As Double = burn.GetCurrentWidth()
        Dim width As Double = Size.Width()
        Dim move As Double = width / DIVISIONS

        Dim till As Double = (width / MAX_CAPACITY) * slid_width
        Dim full As Double = (width / MAX_CAPACITY) * FULL_CAPACITY

        If slid_width > FULL_CAPACITY

            painter.SetPen(New QPen(New QBrush(yellowColor), 1))
            painter.SetBrush(New QBrush(yellowColor))
            painter.DrawRect(New QRectF(0, 0, full, PANEL_HEIGHT))
            painter.SetPen(New QPen(New QBrush(redColor), 1))
            painter.SetBrush(New QBrush(redColor))
            painter.DrawRect(New QRectF(full+1, 0, till-full, PANEL_HEIGHT))

        Else
        
            If (slid_width > 0) 
               painter.SetPen(New QPen(New QBrush(yellowColor), 1))
               painter.SetBrush(New QBrush(yellowColor))
               painter.DrawRect(New QRectF(0, 0, till, PANEL_HEIGHT))
            End If
            
        End If
        
        
        painter.SetPen(New QColor(90, 90, 90))
        painter.SetBrush(BrushStyle.NoBrush)
        painter.DrawRect(0, 0, Size.Width()-1, PANEL_HEIGHT-1)
        
        Dim newFont As QFont = painter.Font()
        newFont.SetPointSize(7)
        painter.SetFont(newFont)

        Dim metrics As New QFontMetrics(newFont)

        For i As Integer = 1 to num.Length
            painter.DrawLine(New QLineF(i*move, 1, i*move, LINE_WIDTH))

            Dim w As Integer = metrics.Width(num(i-1))

            painter.DrawText(New QPointF(i*move-w/2, DISTANCE), num(i-1))
        Next
        
    End Sub
    
End Class
    
End Namespace
</pre>

<p>
In this file, we create the Burning widget. 
</p>


<pre class="explanation">
Public Class Burning 
    Inherits QWidget
</pre>

<p>
The custom widget is based on the <b class="keyword">QWidget</b> widget. 
</p>

<pre class="explanation">
Const PANEL_HEIGHT As Integer = 30
Const DISTANCE As Integer = 19
Const LINE_WIDTH As Integer = 5
Const DIVISIONS As Integer = 10

Const FULL_CAPACITY As Double = 700.0
Const MAX_CAPACITY As Double = 750.0
</pre>

<p>
These are important constants. The <b class="keyword">PANEL_HEIGHT</b> defines the height
for the custom widget. 
The <b class="keyword">DISTANCE</b> is the distance of the numbers on the scale from the top
of their parent border. The <b class="keyword">LINE_WIDTH</b> is the vertical line width.
The <b class="keyword">DIVISIONS</b> is the number of parts of the
scale. The <b class="keyword">FULL_CAPACITY</b> is the capacity of the media. After
it is reached, overburning happens. This is visualized by a red color. 
The <b class="keyword">MAX_CAPACITY</b> is the maximum capacity of a medium. 
</p>

<pre class="explanation">
Dim num() As String = { _
    "75", "150", "225", "300", _
    "375", "450", "525", "600", _
    "675" _
}
</pre>

<p>
We use these numbers to build the scale of the Burning widget. 
</p>

<pre class="explanation">
Protected Overrides Sub PaintEvent(ByVal e As QPaintEvent) 

    Dim painter As New QPainter(Me)
    
    Me.DrawWidget(painter)
    painter.End()
    
End Sub
</pre>

<p>
The drawing of the custom widget is delegated to the <b class="keyword">DrawWidget()</b> method.
</p>

<pre class="explanation">
Dim burn As CustomWidget.VBQApp = CType(parent, CustomWidget.VBQApp)
</pre>

<p>
We retrieve the reference to the parent widget. 
</p>

<pre class="explanation">
Dim slid_width As Double = burn.GetCurrentWidth()
</pre>

<p>
We use it to get the currently selected slider value.
</p>

<pre class="explanation">
Dim width As Double = Size.Width()
</pre>

<p>
We get the width of the widget. The width of the custom widget is dynamic. It can be
resized by a user. 
</p>

<pre class="explanation">
Dim till As Double = (width / MAX_CAPACITY) * slid_width
Dim full As Double = (width / MAX_CAPACITY) * FULL_CAPACITY
</pre>

<p>
We use the <b class="keyword">width</b> variable to do the transformations. Between 
the values of the scale and the custom widget's measures. Note that we use floating
point values. We get greater precision in drawing. 
</p>

<pre class="explanation">
painter.SetPen(New QPen(New QBrush(redColor), 1))
painter.SetBrush(New QBrush(redColor))
painter.DrawRect(New QRectF(full+1, 0, till-full, PANEL_HEIGHT))
</pre>

<p>
These three lines draw the red rectangle, indicating the overburning. 
</p>

<pre class="explanation">
painter.DrawRect(0, 0, Size.Width()-1, PANEL_HEIGHT-1)
</pre>

<p>
This is the perimeter of the widget. The outside rectangle. 
</p>

<pre class="explanation">
painter.DrawLine(New QLineF(i*move, 1, i*move, LINE_WIDTH))
</pre>

<p>
Here we draw the small vertical lines. 
</p>

<pre class="explanation">
Dim w As Integer = metrics.Width(num(i-1))

painter.DrawText(New QPointF(i*move-w/2, DISTANCE), num(i-1))
</pre>

<p>
Here we draw the numbers of the scale. To precisely position the numbers,
we must get the width of the string. 
</p>


<div class="codehead">main.vb</div>
<pre class="code">
Imports Qyoto

' ZetCode Mono Visual Basic Qt tutorial
'
' In this program, we create
' a custom widget
'
' @author jan bodnar
' website zetcode.com
' last modified May 2009

NameSpace CustomWidget

Public Class VBQApp 
    Inherits QWidget

    Const MAX_CAPACITY As Integer = 750

    Dim slider As QSlider
    Dim widget As QWidget
    Dim cur_width As Integer

    Public Sub New()
        Me.SetWindowTitle("The Burning Widget")

        Me.InitUI()

        Me.Resize(370, 200)
        Me.Move(300, 300)
        Me.Show()
    End Sub

    Private Sub InitUI() 
       
       slider = New QSlider(Qt.Orientation.Horizontal , Me)
       slider.Maximum = MAX_CAPACITY
       slider.SetGeometry(50, 50, 130, 30)

       Connect(slider, SIGNAL("valueChanged(int)"), Me, _
                SLOT("ValueChanged(int)"))
       
       Dim vbox As New QVBoxLayout(Me)
       Dim hbox As New QHBoxLayout

       vbox.AddStretch(1)

       widget = New Burning.Burning(Me)
       hbox.AddWidget(widget, 0)

       vbox.AddLayout(hbox)

       SetLayout(vbox)
       
    End Sub

    &lt;Q_SLOT()&gt; _
    Public Sub ValueChanged(ByVal val As Integer) 
        cur_width = val
        widget.Repaint()
    End Sub
    

    Public Function GetCurrentWidth() As Integer
      Return cur_width
    End Function
    

    Public Shared Sub Main(ByVal args() As String)
        Dim qapp As New QApplication(args)
        Dim app As New VBQApp
        QApplication.Exec()
    End Sub

End Class

NameSpace CustomWidget
</pre>

<p>
This is the main file. Here we create the slider widget and use our
custom widget. 
</p>

<pre class="explanation">
widget = New Burning.Burning(Me)
hbox.AddWidget(widget, 0)
</pre>

<p>
We create the instance of the Burning widget and add
it to the horizontal box. 
</p>

<pre class="explanation">
&lt;Q_SLOT()&gt; _
Public Sub ValueChanged(ByVal val As Integer) 
    cur_width = val
    widget.Repaint()
End Sub
</pre>

<p>
When the value of the slider changes, we store it inside the
<b class="keyword">cur_width</b> variable and repaint the custom widget. 
</p>

<pre class="explanation">
Public Function GetCurrentWidth() As Integer
  Return cur_width
End Function
</pre>

<p>
This method is called by the custom widget to get the actual slider
value. 
</p>

<br>
<img src="/img/gui/vbqyoto/burning.png" alt="The Burning widget">
<div class="figure">Figure: The Burning widget</div>

<p>
In this part of the Visual Basic Qyoto tutorial, we have demonstrated how to create a custom
widget. 
</p>

<hr class="btm">

<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div> 
<br> 

<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡ 
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified May 17, 2009  <span class="copyright">&copy; 2007 - 2012 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>
